#include "gtest/gtest.h"

#include <string>
#include <sstream>

namespace test{
class Result {
public:
    enum class Code  : unsigned char {
        kOK = 0,
        kInvalidArgument = 1,
        kArgumentNull = 2,
        kNotSupport = 3,
        kIOError = 4,
        kMaxCode,
    };

    virtual ~Result() {}

    constexpr Code code() const { return _code; }

    bool isOK() const { return _code == Code::kOK; }
    bool isInvalidArgument() const  { return _code == Code::kInvalidArgument; }
    bool isArgumentNull() const  { return _code == Code::kArgumentNull; }
    bool isNotSupport() const  { return _code == Code::kNotSupport; }
    bool isIOError() const { return _code == Code::kIOError; }

    static Result OK(){ return Result(); }
    static Result InvalidArgument(const std::string_view & msg){
        return Result(Code::kInvalidArgument, msg);
    }
    static Result ArgumentNull(const std::string_view & msg){
        return Result(Code::kArgumentNull, msg);
    }
    static Result NotSupport(const std::string_view & msg){
        return Result(Code::kNotSupport, msg);
    }

    virtual const std::string & what() const;
protected:
    Result(Code code = Code::kOK, int subcode = 0)
    : _code(code), _subcode(subcode){
    }
    Result(Code code, const std::string_view & msg, int subcode = 0) 
    : Result(code, subcode){
        state((_code == Code::kOK) ? "OK": getState(_code, _subcode, msg));
    }

    const char * getCodeMsg(Code code) const;
    virtual const char * getSubcodeMsg(int subcode) const; //override by subclasses
    std::string getState(Code code, int subcode, const std::string_view & msg) const {
        std::ostringstream oss;
        if(auto * codeMsg = getCodeMsg(code); codeMsg != nullptr){
            oss << codeMsg << " : ";
            if(auto * subcodeMsg = getSubcodeMsg(subcode); subcodeMsg != nullptr){
                oss << subcodeMsg;
            }
            else{
                oss << "unknown subcode Msg-" << subcode;
            }
            oss << " : " << msg;
        }
        else{
            oss << "Unknown Code Msg-" << static_cast<int>(code) << std::endl;
        }
        return oss.str();
    }
    constexpr int subcode() const {
        return _subcode;
    }
    const std::string & getState() const {
        return _state;
    }
    void state(const std::string & _state){
        this->_state = _state;
    }
protected:
    Code _code; 
    int _subcode;
    std::string _state; //optimize to const char *;
};

const char * Result::getCodeMsg(Code code) const{
    static const char * CodeMessage[static_cast<int>(Result::Code::kMaxCode)] {
        "OK",
        "InvalidArgument",
        "ArgumentNull",
        "NotSupport",
        "IOError",
    };
    if(static_cast<int>(code) >= 0 && code < Code::kMaxCode){
        return CodeMessage[static_cast<int>(code)];
    }
    return nullptr;
}
const char * Result::getSubcodeMsg(int subcode) const{
    if(subcode == 0){
        return "None";
    }
    return nullptr;
}

const std::string & Result::what() const {
    return _state;
}
}// end of namespace test

using namespace test;
TEST(ResultTest, testMain){
    {
        Result s = Result::OK();
        ASSERT_TRUE(s.isOK());
        std::cout << s.what() << std::endl;
    }

    {
        auto s = Result::InvalidArgument("1st arg is error");
        ASSERT_FALSE(s.isOK());
        std::cout << s.what() << std::endl;
    }
}


namespace test {
class InvalidArgumentResult : public Result{
public:
    using Code = Result::Code;
    enum class Subcode : unsigned char{
        kNone = 0,
        kTypeError =1,
        kMustbeString =2,
        kMaxSubcode,
    };

    ~InvalidArgumentResult() override{
    }
    bool isTypeError()const { return subcode() == static_cast<int>(Subcode::kTypeError);}
    bool isMustbeString()const { return subcode() == static_cast<int>(Subcode::kMustbeString);}

    static InvalidArgumentResult OK(){
        return InvalidArgumentResult();
    }
    static InvalidArgumentResult TypeError(const std::string & msg){
        return InvalidArgumentResult(Subcode::kTypeError, msg);
    }
    static InvalidArgumentResult MustbeString(const std::string & msg){
        return InvalidArgumentResult(Subcode::kMustbeString, msg);
    }
    static InvalidArgumentResult fromResult(Result && result){
        if(result.isOK()){
            return InvalidArgumentResult::OK();
        }
        //TODO:
        return InvalidArgumentResult::OK();
    }
protected:
    const char * getSubcodeMsg(int subcode) const override {
        static const char * iaSubcodeMsg[static_cast<int>(InvalidArgumentResult::Subcode::kMaxSubcode)] = {
            "None",
            "TypeError",
            "MustbeString",
        };
        if(subcode >= 0 && subcode < static_cast<int>(InvalidArgumentResult::Subcode::kMaxSubcode)){
            return iaSubcodeMsg[subcode];
        }
        return nullptr;
    }
    InvalidArgumentResult()
    :Result()
    {
        state("OK");
    }
    InvalidArgumentResult(Subcode subcode, const std::string_view & msg)
    : Result(Code::kInvalidArgument, static_cast<int>(subcode)){ //Note????
        state(getState(_code, _subcode, msg)); //must call it here
    }
private:
};
}// end of namespace test

TEST(InvalidArgumentResultTest, testMain){
    InvalidArgumentResult r = InvalidArgumentResult::TypeError("double is needed");
    ASSERT_FALSE(r.isOK());
    std::cout << r.what() << std::endl;
}


class AllResultTest : public testing::Test {
public:
    Result operation(){
        return Result::OK();
    }

    Result result_from_subResult(){
        auto iar = InvalidArgumentResult::TypeError(""); // code=IOError, subcode=Busy
        if(!iar.isOK()){ 
            std::cout << iar.what() << std::endl;
            return iar;//  result = InvalidArgumentResult;
        }
        return Result::OK();
    }

    // Sub s = Base();
    InvalidArgumentResult subResult_from_result(){
        return InvalidArgumentResult::fromResult(Result::OK()); 
    }
};
